package com.kinghunt.misp.service.impl.userx;

import com.alibaba.fastjson.JSONObject;
import com.kinghunt.misp.common.constant.ConfigHolder;
import com.kinghunt.misp.common.dto.userx.WeChatSessionDTO;
import com.kinghunt.misp.common.utils.TokenUtils;
import com.kinghunt.misp.dal.dao.userx.UserDAO;
import com.kinghunt.misp.dal.dao.userx.WeChatSessionDAO;
import com.kinghunt.misp.dal.model.userx.UserDO;
import com.kinghunt.misp.dal.model.userx.WeChatSessionDO;
import com.kinghunt.misp.service.api.userx.WeChatService;
import com.bygoodtech.kings.common.dto.ApiResult;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.Date;
import java.util.Objects;

@Slf4j
@Service
public class WeChatServiceImpl implements WeChatService {
    @Value("${wechat.appId}")
    private String appId;
    @Value("${wechat.secret}")
    private String secret;

    private final ConfigHolder configHolder;

    private final WeChatSessionDAO weChatSessionDAO;
    private final UserDAO userDAO;

    public WeChatServiceImpl(ConfigHolder configHolder,
                             WeChatSessionDAO weChatSessionDAO,
                             UserDAO userDAO) {
        this.configHolder = configHolder;
        this.weChatSessionDAO = weChatSessionDAO;
        this.userDAO = userDAO;
    }

    @Override
    public ApiResult<WeChatSessionDTO> code2Session(String code) {
        log.info("code2Session : {}", code);
        WeChatSessionDTO weChatSessionDTO = getJscode2Session(appId, secret, code);

        String openId = weChatSessionDTO.getOpenid();
        String sessionKey = weChatSessionDTO.getSession_key();
        if(StringUtils.isEmpty(openId) || StringUtils.isEmpty(sessionKey)){
            return ApiResult.fail("-1", "cannot get userInfo from wechat!");
        }

        WeChatSessionDO data = weChatSessionDAO.selectByOpenId(openId);
        if (Objects.isNull(data)) {
            // 将openId - sessionKey 入库保存，后续获取授权手机号时需要使用
            WeChatSessionDO weChatSessionDO = new WeChatSessionDO();
            weChatSessionDO.setOpenId(openId);
            weChatSessionDO.setSessionKey(sessionKey);
            weChatSessionDO.setCreateGmt(new Date());
            weChatSessionDO.setUpdateGmt(new Date());
            weChatSessionDO.setIsDel(Boolean.FALSE);
            weChatSessionDAO.insertSelective(weChatSessionDO);
        } else {
            // 及时更新session，防止过期
            data.setSessionKey(sessionKey);
            data.setUpdateGmt(new Date());
            weChatSessionDAO.updateByPrimaryKey(data);
        }

        UserDO user = userDAO.selectByOpenId(openId);
        if (Objects.nonNull(user)) {
            weChatSessionDTO.setUserId(user.getId());
            weChatSessionDTO.setAuthorization(TokenUtils.toToken(user.getId()));
        }

        return ApiResult.ok(weChatSessionDTO);
    }

    @Override
    public ApiResult<String> getWeChatPhone(String iv, String encryptedData, String openId) {
        WeChatSessionDO weChatSessionDO = weChatSessionDAO.selectByOpenId(openId);
        if (Objects.isNull(weChatSessionDO)) {
            return ApiResult.fail("-1", "cannot get phone from wechat!");
        }

        String sessionKey = weChatSessionDO.getSessionKey();
        String phone = decryptPhone(encryptedData, sessionKey, iv);
        if (StringUtils.isEmpty(phone)) {
            return ApiResult.fail("-1", "cannot get phone from wechat!");
        }
        return ApiResult.ok(phone);
    }


    private WeChatSessionDTO getJscode2Session(String appId,String secret,String code){
        String url = configHolder.getJscode2sessionURL()+"?appid="+appId
                +"&secret="+secret+"&js_code="+code+"&grant_type=authorization_code";
        log.info("getJscode2Session url {}", url);
        URI uri = URI.create(url);
        HttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(uri);
        HttpResponse response;
        WeChatSessionDTO weChatSessionDTO = new WeChatSessionDTO();
        try {
            response = client.execute(get);
            if (response.getStatusLine().getStatusCode() == 200) {
                HttpEntity entity = response.getEntity();
                BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
                StringBuilder sb = new StringBuilder();
                for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
                    sb.append(temp);
                }
                JSONObject object = JSONObject.parseObject(sb.toString().trim());
                log.info("code2Session result : {}", sb.toString());
                weChatSessionDTO.setSession_key((String)object.get("session_key"));
                weChatSessionDTO.setOpenid((String)object.get("openid"));
                weChatSessionDTO.setErrcode((Number)object.get("errcode"));
                weChatSessionDTO.setErrmsg((String)object.get("errmsg"));
                weChatSessionDTO.setUnionid((String)object.get("unionid"));
            }
        } catch (Exception e) {
            log.error("getAccessToken error!",e);
        }
        return weChatSessionDTO;
    }


    /**
     * 解密微信授权手机号
     *
     * @param encryptedData
     * @param session_key
     * @param iv
     * @return
     */
    private String decryptPhone(String encryptedData, String session_key, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(session_key);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance(configHolder.getAlgorithmSTR());
            SecretKeySpec spec = new SecretKeySpec(keyByte, configHolder.getAlgorithmKey());
            AlgorithmParameters parameters = AlgorithmParameters.getInstance(configHolder.getAlgorithmKey());
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                JSONObject object = JSONObject.parseObject(result.trim());
                String phoneNum = object.getString("phoneNumber");
                return phoneNum;
            }
        } catch (Exception e) {
            log.error("getPhoneNumber error!",e);
        }
        return null;
    }
}
